package org.eclipse.refactoring.localvariable;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;


import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaConventions;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.ASTRequestor;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.ArrayAccess;
import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.AssertStatement;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BreakStatement;
import org.eclipse.jdt.core.dom.CastExpression;
import org.eclipse.jdt.core.dom.CatchClause;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ConditionalExpression;
import org.eclipse.jdt.core.dom.ConstructorInvocation;
import org.eclipse.jdt.core.dom.DoStatement;
import org.eclipse.jdt.core.dom.EmptyStatement;
import org.eclipse.jdt.core.dom.EnhancedForStatement;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.ExpressionStatement;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.ForStatement;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.IfStatement;
import org.eclipse.jdt.core.dom.InfixExpression;
import org.eclipse.jdt.core.dom.InstanceofExpression;
import org.eclipse.jdt.core.dom.Javadoc;
import org.eclipse.jdt.core.dom.LabeledStatement;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.NodeFinder;
import org.eclipse.jdt.core.dom.ParenthesizedExpression;
import org.eclipse.jdt.core.dom.PostfixExpression;
import org.eclipse.jdt.core.dom.PrefixExpression;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
import org.eclipse.jdt.core.dom.SuperFieldAccess;
import org.eclipse.jdt.core.dom.SuperMethodInvocation;
import org.eclipse.jdt.core.dom.SwitchCase;
import org.eclipse.jdt.core.dom.SwitchStatement;
import org.eclipse.jdt.core.dom.SynchronizedStatement;
import org.eclipse.jdt.core.dom.TagElement;
import org.eclipse.jdt.core.dom.ThrowStatement;
import org.eclipse.jdt.core.dom.TryStatement;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import org.eclipse.jdt.core.dom.WhileStatement;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.ui.JavaElementLabels;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.ChangeDescriptor;
import org.eclipse.ltk.core.refactoring.CompositeChange;
import org.eclipse.ltk.core.refactoring.Refactoring;
import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.TextFileChange;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.TextEdit;

public class PrefixRefactorRefactoring extends Refactoring {

	private static final String CLASS = "class";

	private static final String REFERENCES = "references";

	private static final String PACKAGE = "package";

	private static final String FIELD_PREFIX = "m";

	private static final String PARAMETER_PREFIX = "p";

	private static final String LOCAL_VAR_PREFIX = "l";

	private static final String STATIC_FIELD_PREFIX = "g";

	private Map<ICompilationUnit, TextFileChange> mChanges = null;

	ICompilationUnit mICompilationUnit = null;

	List<ICompilationUnit> mICompilationUnits = null;

	IPackageFragment mIPackageFragment = null;

	// String mName = null;

	boolean mUpdateReferences = true;

	@Override
	public RefactoringStatus checkFinalConditions(IProgressMonitor monitor)
			throws CoreException, OperationCanceledException {
		final RefactoringStatus status = new RefactoringStatus();
		try {
			monitor.beginTask("Checking preconditions...", 2);
			mChanges = new LinkedHashMap<ICompilationUnit, TextFileChange>();

			final Map<IJavaProject, Set<ICompilationUnit>> projects = new HashMap<IJavaProject, Set<ICompilationUnit>>();

			for (ICompilationUnit lICompilationUnit : mICompilationUnits) {
				IJavaProject lProject = lICompilationUnit.getJavaProject();
				Set<ICompilationUnit> lCompilationUnits = projects
						.get(lProject);
				if (lCompilationUnits == null) {
					lCompilationUnits = new HashSet<ICompilationUnit>();
					projects.put(lProject, lCompilationUnits);
				}
				lCompilationUnits.add(lICompilationUnit);
			}

			// Create the requestor for changing compilation unit
			ASTRequestor requestors = new ASTRequestor() {

				@Override
				public void acceptAST(ICompilationUnit source,
						CompilationUnit ast) {
					try {

						ASTRewrite astRewrite = ASTRewrite.create(ast.getAST());
						ast.accept(new MethodVisitor(astRewrite));

						ImportRewrite importRewrite = ImportRewrite.create(
								source, true);
						rewriteAST(source, astRewrite, importRewrite);
					} catch (JavaModelException e) {
						RefactoringPlugin.log(e);
					}
				}
			};

			IProgressMonitor subMonitor = new SubProgressMonitor(monitor, 1);

			try {
				final Set<IJavaProject> set = projects.keySet();
				subMonitor.beginTask("Compiling source...", set.size());

				for (IJavaProject project : set) {
					ASTParser parser = ASTParser.newParser(AST.JLS3);
					parser.setProject(project);
					parser.setResolveBindings(true);
					Set<ICompilationUnit> collection = projects.get(project);
					parser.createASTs(collection
							.toArray(new ICompilationUnit[collection.size()]),
							new String[0], requestors, new SubProgressMonitor(
									subMonitor, 1));
				}

			} finally {
				subMonitor.done();
			}

		} finally {
			monitor.done();
		}
		return status;
	}

	private boolean hasToBeRenammed(String pName, String pPrefix) {
		boolean lResult = true;

		if (pName.startsWith(pPrefix)) {
			if (pName.length() > 1) {
				String lSecondChar = String.valueOf(pName.charAt(1));
				if (lSecondChar.equals(lSecondChar.toUpperCase())) {
					lResult = false;
				}
			}
		}

		return lResult;
	}

//	private boolean hasToRenammeField(IField pIField) {
//		String lFieldName = pIField.getElementName();
//		try {
//
//			return !pIField.getDeclaringType().isEnum()
//					&& !pIField.getDeclaringType().isInterface()
//					&& !lFieldName.equals("serialVersionUID")
//					&& ((Flags.isStatic(pIField.getFlags())
//							&& !Flags.isFinal(pIField.getFlags()) && hasToBeRenammed(
//								lFieldName, STATIC_FIELD_PREFIX)) || (!Flags
//							.isStatic(pIField.getFlags()) && hasToBeRenammed(
//							lFieldName, FIELD_PREFIX)));
//		} catch (JavaModelException e) {
//			e.printStackTrace();
//			return false;
//		}
//	}

	@Override
	public RefactoringStatus checkInitialConditions(IProgressMonitor monitor)
			throws CoreException, OperationCanceledException {
		RefactoringStatus status = new RefactoringStatus();
		try {
			monitor.beginTask("Checking preconditions...", 1);
			if (mICompilationUnit == null && mIPackageFragment == null)
				status.merge(RefactoringStatus
						.createFatalErrorStatus("Neither ICompilationUnit nor IPackageFragement has not been specified."));
			else if (mICompilationUnit != null && !mICompilationUnit.exists())
				status.merge(RefactoringStatus
						.createFatalErrorStatus(MessageFormat.format(
								"ICompilationUnit ''{0}'' does not exist.",
								new Object[] { mICompilationUnit
										.getElementName() })));
			else if (mIPackageFragment != null && !mIPackageFragment.exists())
				status.merge(RefactoringStatus
						.createFatalErrorStatus(MessageFormat.format(
								"IPackageFragment ''{0}'' does not exist.",
								new Object[] { mIPackageFragment
										.getElementName() })));
			else if (mICompilationUnit != null
					&& !mICompilationUnit.isStructureKnown())
				status.merge(RefactoringStatus.createFatalErrorStatus(MessageFormat
						.format("Compilation unit ''{0}'' contains compile errors.",
								new Object[] { mICompilationUnit
										.getElementName() })));
			else if (mIPackageFragment != null
					&& !mIPackageFragment.isStructureKnown())
				status.merge(RefactoringStatus.createFatalErrorStatus(MessageFormat
						.format("Package fragment ''{0}'' contains compile errors.",
								new Object[] { mIPackageFragment
										.getElementName() })));
		} finally {
			monitor.done();
		}
		return status;
	}

	private RefactoringStatus convertIStatus(IStatus status) {
		RefactoringStatus result = new RefactoringStatus();
		if (status == null) //$NON-NLS-1$
			return RefactoringStatus
					.createFatalErrorStatus("Choose a Java file or a package.");

		if (status.isOK())
			return result;

		switch (status.getSeverity()) {
		case IStatus.ERROR:
			return RefactoringStatus
					.createFatalErrorStatus(status.getMessage());
		case IStatus.WARNING:
			return RefactoringStatus.createWarningStatus(status.getMessage());
		case IStatus.INFO:
			return RefactoringStatus.createInfoStatus(status.getMessage());
		default: // no nothing
			return new RefactoringStatus();
		}
	}

	@Override
	public Change createChange(IProgressMonitor monitor) throws CoreException,
			OperationCanceledException {
		try {
			monitor.beginTask("Creating change...", 1);
			final Collection<TextFileChange> changes = mChanges.values();
			CompositeChange change = new CompositeChange(getName(),
					changes.toArray(new Change[changes.size()])) {

				@Override
				public ChangeDescriptor getDescriptor() {

					String project = mICompilationUnits.get(0).getJavaProject()
							.getElementName();
					String description = null;
					if (mICompilationUnit != null) {
						description = MessageFormat.format(
								"Renaming field on ''{0}''",
								new Object[] { mICompilationUnit
										.getElementName() });
					} else {
						description = MessageFormat.format(
								"Renaming field on ''{0}''",
								new Object[] { mIPackageFragment
										.getElementName() });
					}
					String classLabel = null;

					if (mICompilationUnit != null) {
						classLabel = JavaElementLabels.getTextLabel(
								mICompilationUnit,
								JavaElementLabels.ALL_FULLY_QUALIFIED);
					} else {
						classLabel = JavaElementLabels.getTextLabel(
								mIPackageFragment,
								JavaElementLabels.ALL_FULLY_QUALIFIED);
					}
					// String typeLabel= JavaElementLabels.getTextLabel(fType,
					// JavaElementLabels.ALL_FULLY_QUALIFIED);
					String comment = MessageFormat.format(
							"Renaming field on ''{0}''",
							new Object[] { classLabel });
					Map<String, String> arguments = new HashMap<String, String>();
					if (mICompilationUnit != null) {
						arguments.put(CLASS,
								mICompilationUnit.getHandleIdentifier());
					}
					if (mIPackageFragment != null) {
						arguments.put(PACKAGE,
								mIPackageFragment.getHandleIdentifier());
					}
					// arguments.put(TYPE, fType.getHandleIdentifier());
					// arguments.put(NAME, fName);
					arguments.put(REFERENCES, Boolean
							.valueOf(mUpdateReferences).toString());
					return new RefactoringChangeDescriptor(
							new PrefixRefactorDescriptor(project, description,
									comment, arguments));
				}
			};
			return change;
		} finally {
			monitor.done();
		}
	}

	@Override
	public String getName() {
		return "Refactor Fields";
	}

	public RefactoringStatus initialize(Map arguments) {
		RefactoringStatus status = new RefactoringStatus();
		String value = (String) arguments.get(CLASS);
		if (value != null)
			setCompilationUnit((ICompilationUnit) JavaCore.create(value));
		// value= (String) arguments.get(TYPE);
		// if (value != null)
		// setDeclaringType((IType) JavaCore.create(value));
		// value= (String) arguments.get(NAME);
		// if (value != null)
		// setMethodName(value);
		value = (String) arguments.get(REFERENCES);
		if (value != null)
			setUpdateReferences(Boolean.valueOf(value).booleanValue());

		value = (String) arguments.get(PACKAGE);
		if (value != null)
			setPackageFragment((IPackageFragment) JavaCore.create(value));

		return status;
	}

	private boolean isEmptyEdit(TextEdit edit) {
		return edit.getClass() == MultiTextEdit.class && !edit.hasChildren();
	}

	private void rewriteAST(ICompilationUnit unit, ASTRewrite astRewrite,
			ImportRewrite importRewrite) {
		try {
			MultiTextEdit edit = new MultiTextEdit();
			TextEdit astEdit = astRewrite.rewriteAST();

			if (!isEmptyEdit(astEdit))
				edit.addChild(astEdit);
			TextEdit importEdit = importRewrite
					.rewriteImports(new NullProgressMonitor());
			if (!isEmptyEdit(importEdit))
				edit.addChild(importEdit);
			if (isEmptyEdit(edit))
				return;

			TextFileChange change = mChanges.get(unit);
			if (change == null) {
				change = new TextFileChange(unit.getElementName(),
						(IFile) unit.getResource());
				change.setTextType("java");
				change.setEdit(edit);
			} else
				change.getEdit().addChild(edit);

			mChanges.put(unit, change);
		} catch (MalformedTreeException exception) {
			RefactoringPlugin.log(exception);
		} catch (IllegalArgumentException exception) {
			RefactoringPlugin.log(exception);
		} catch (CoreException exception) {
			RefactoringPlugin.log(exception);
		}
	}

	protected void rewriteCompilationUnit(ASTRequestor requestor,
			ICompilationUnit unit,
			Map<IField, Collection<ICompilationUnit>> lReferencingUnits,
			Map<ICompilationUnit, Collection<SearchMatch>> units,
			CompilationUnit node, RefactoringStatus status,
			List<IField> lFieldList) throws CoreException {
		ASTRewrite astRewrite = ASTRewrite.create(node.getAST());
		ImportRewrite importRewrite = ImportRewrite.create(node, true);

		for (IField lIField : lFieldList) {
			if (unit.equals(lIField.getDeclaringType().getCompilationUnit())) {
				rewriteDeclaringType(requestor, astRewrite, importRewrite,
						unit, node, lIField);
			}
			if (mUpdateReferences) {
				for (ICompilationUnit lICompilationUnit : lReferencingUnits
						.get(lIField)) {
					for (final Iterator<SearchMatch> iterator = units.get(
							lICompilationUnit).iterator(); iterator.hasNext();) {
						SearchMatch match = iterator.next();
						if (match.getAccuracy() == SearchMatch.A_ACCURATE) {
							ASTNode result = NodeFinder.perform(node,
									match.getOffset(), match.getLength());
							if (result instanceof SimpleName
									&& ((SimpleName) result).getIdentifier()
											.equals(lIField.getElementName())) {
								status.merge(rewriteFieldAccess(requestor,
										astRewrite, importRewrite, lIField,
										(SimpleName) result));
							}
						}
					}
				}
			}
		}
		rewriteAST(unit, astRewrite, importRewrite);
	}

	private void rewriteDeclaringType(ASTRequestor requestor,
			ASTRewrite astRewrite, ImportRewrite importRewrite,
			ICompilationUnit unit, CompilationUnit node, IField pIField)
			throws CoreException {

		IVariableBinding lVariableBinding = null;
		ITypeBinding lDeclaringType = null;
		IBinding[] bindings = requestor.createBindings(new String[] { pIField
				.getKey() });
		if (bindings[0] instanceof IVariableBinding) {
			lVariableBinding = (IVariableBinding) bindings[0];
			if (lVariableBinding != null && lVariableBinding.isField()) {
				lDeclaringType = lVariableBinding.getDeclaringClass();
			}
		}

		if (lVariableBinding == null || lDeclaringType == null)
			return;

		AST ast = node.getAST();

		// Field declaration
		VariableDeclarationFragment fragment = ast
				.newVariableDeclarationFragment();

		fragment.setName(ast.newSimpleName(getRenammedField(pIField)));
		// fragment.setFlags(pIField.getFlags());

		ASTNode lVariableNode = node.findDeclaringNode(lVariableBinding);

		if (lVariableNode instanceof VariableDeclarationFragment) {
			VariableDeclarationFragment lVariableDeclarationFragment = (VariableDeclarationFragment) lVariableNode;
			Expression lExpression = lVariableDeclarationFragment
					.getInitializer();

			if (lExpression != null) {
				fragment.setInitializer((Expression) astRewrite
						.createCopyTarget(lExpression));
			}
		}

		astRewrite.replace(lVariableNode, fragment, null);

	}

	private String getRenammedField(IField pIField) throws JavaModelException {
		String lNewFieldName = null;
		if (Flags.isStatic(pIField.getFlags())) {
			lNewFieldName = getRefactoredName(pIField.getElementName(),
					STATIC_FIELD_PREFIX);
		} else {
			lNewFieldName = getRefactoredName(pIField.getElementName(),
					FIELD_PREFIX);
		}
		return lNewFieldName;
	}

	private RefactoringStatus rewriteFieldAccess(ASTRequestor requestor,
			ASTRewrite astRewrite, ImportRewrite importRewrite, IField pIField,
			SimpleName result) throws JavaModelException {
		RefactoringStatus status = new RefactoringStatus();

		ITypeBinding declaringBinding = null;
		IBinding[] bindings = requestor.createBindings(new String[] { pIField
				.getDeclaringType().getKey() });
		if (bindings[0] instanceof ITypeBinding) {
			declaringBinding = (ITypeBinding) bindings[0];
		}

		if (declaringBinding == null)
			return status;

		AST ast = result.getAST();

		SimpleName newSimpleName = ast.newSimpleName(getRenammedField(pIField));

		astRewrite.replace(result, newSimpleName, null);

		return status;
	}

	private String getRefactoredName(String pName, String pPrefix) {
		String lResult = "";
		lResult = pPrefix + String.valueOf(pName.charAt(0)).toUpperCase()
				+ pName.substring(1);
		return lResult;
	}

	// public RefactoringStatus setClassName(String name) {
	// mName = name;
	// RefactoringStatus status = checkClassName(name,
	// JavaConventions.validateCompilationUnitName(name,
	// CompilerOptions.VERSION_1_3,
	// CompilerOptions.VERSION_1_3));
	// // status.merge(checkOverloading());
	// return status;
	// }

	public RefactoringStatus checkInput() {
		IStatus lIStatus = null;
		if (mICompilationUnit != null) {
			lIStatus = JavaConventions.validateCompilationUnitName(
					mICompilationUnit.getElementName(),
					CompilerOptions.VERSION_1_6, CompilerOptions.VERSION_1_6);
		} else if (mIPackageFragment != null) {
			lIStatus = JavaConventions.validatePackageName(
					mIPackageFragment.getElementName(),
					CompilerOptions.VERSION_1_6, CompilerOptions.VERSION_1_6);
		}
		return convertIStatus(lIStatus);
	}

	public void setUpdateReferences(boolean update) {
		mUpdateReferences = update;
	}

	public void setCompilationUnit(ICompilationUnit pICompilationUnit) {
		mICompilationUnit = pICompilationUnit;
		mICompilationUnits = new ArrayList<ICompilationUnit>();
		mICompilationUnits.add(mICompilationUnit);
	}

	public IJavaElement getCompilationUnit() {
		return mICompilationUnit;
	}

	private class MethodVisitor extends ASTVisitor {
		List<MethodDeclaration> methods = new ArrayList<MethodDeclaration>();
		ASTRewrite mASTRewrite;
		Map<String, String> mRefactoredVariables = new HashMap<String, String>();

		public MethodVisitor(ASTRewrite astRewrite) {
			mASTRewrite = astRewrite;
		}

		public void lookThroughStatement(MethodDeclaration node,
				Statement statement) {
			if (statement instanceof VariableDeclarationStatement) {
				VariableDeclarationStatement lVariableDeclarationStatement = (VariableDeclarationStatement) statement;
				List<VariableDeclarationFragment> lVariableDeclarationFragments = lVariableDeclarationStatement
						.fragments();
				for (VariableDeclarationFragment variableDeclarationFragment : lVariableDeclarationFragments) {

					lookThroughVariableDeclarationFragment(node,
							variableDeclarationFragment);


				}
			} else if (statement instanceof ExpressionStatement) {
				ExpressionStatement lExpressionStatement = (ExpressionStatement) statement;
				lookThroughExpression(node,
						lExpressionStatement.getExpression());
			} else if (statement instanceof AssertStatement) {
				AssertStatement lStatement = (AssertStatement) statement;
				lookThroughExpression(node, lStatement.getExpression());
			} else if (statement instanceof Block) {
				Block lStatement = (Block) statement;
				for (Object lBlockStatement : lStatement.statements()) {
					lookThroughStatement(node, (Statement) lBlockStatement);
				}
			} else if (statement instanceof ConstructorInvocation) {
				ConstructorInvocation lStatement = (ConstructorInvocation) statement;
				List<Expression> lArguments = lStatement.arguments();
				for (Expression lExpression : lArguments) {
					lookThroughExpression(node, lExpression);
				}
			} else if (statement instanceof SuperConstructorInvocation) {
				SuperConstructorInvocation lStatement = (SuperConstructorInvocation) statement;
				List<Expression> lArguments = lStatement.arguments();
				for (Expression lExpression : lArguments) {
					lookThroughExpression(node, lExpression);
				}
			} else if (statement instanceof DoStatement) {
				DoStatement lStatement = (DoStatement) statement;
				lookThroughStatement(node, lStatement.getBody());
				lookThroughExpression(node, lStatement.getExpression());
			} else if (statement instanceof ForStatement) {
				ForStatement lStatement = (ForStatement) statement;
				for (Object lInitializer : lStatement.initializers()) {
					lookThroughExpression(node, (Expression) lInitializer);
				}
				for (Object lUpdater : lStatement.updaters()) {
					lookThroughExpression(node, (Expression) lUpdater);
				}
				lookThroughExpression(node, lStatement.getExpression());
				lookThroughStatement(node, lStatement.getBody());
			} else if (statement instanceof EnhancedForStatement) {
				EnhancedForStatement lStatement = (EnhancedForStatement) statement;
				lookThroughSingleVariableDeclaration(node,
						lStatement.getParameter(), LOCAL_VAR_PREFIX);
				lookThroughExpression(node, lStatement.getExpression());
				lookThroughStatement(node, lStatement.getBody());
			} else if (statement instanceof IfStatement) {
				IfStatement lStatement = (IfStatement) statement;
				lookThroughStatement(node, lStatement.getElseStatement());
				lookThroughStatement(node, lStatement.getThenStatement());
				lookThroughExpression(node, lStatement.getExpression());
			} else if (statement instanceof WhileStatement) {
				WhileStatement lStatement = (WhileStatement) statement;
				lookThroughStatement(node, lStatement.getBody());
				lookThroughExpression(node, lStatement.getExpression());
			} else if (statement instanceof LabeledStatement) {
				LabeledStatement lStatement = (LabeledStatement) statement;
				lookThroughStatement(node, lStatement.getBody());
			} else if (statement instanceof ReturnStatement) {
				ReturnStatement lStatement = (ReturnStatement) statement;
				lookThroughExpression(node, lStatement.getExpression());
			} else if (statement instanceof SwitchCase) {
				SwitchCase lStatement = (SwitchCase) statement;
				lookThroughExpression(node, lStatement.getExpression());
			} else if (statement instanceof SwitchStatement) {
				SwitchStatement lStatement = (SwitchStatement) statement;
				lookThroughExpression(node, lStatement.getExpression());
				for (Object lBlockStatement : lStatement.statements()) {
					lookThroughStatement(node, (Statement) lBlockStatement);
				}
			} else if (statement instanceof SynchronizedStatement) {
				SynchronizedStatement lStatement = (SynchronizedStatement) statement;
				lookThroughExpression(node, lStatement.getExpression());
				lookThroughStatement(node, lStatement.getBody());
			} else if (statement instanceof ThrowStatement) {
				ThrowStatement lStatement = (ThrowStatement) statement;
				lookThroughExpression(node, lStatement.getExpression());
			} else if (statement instanceof TryStatement) {
				TryStatement lStatement = (TryStatement) statement;
				lookThroughStatement(node, lStatement.getBody());
				lookThroughStatement(node, lStatement.getFinally());
				for (Object lCatch : lStatement.catchClauses()) {
					CatchClause lCatchClause = (CatchClause) lCatch;
					lookThroughSingleVariableDeclaration(node,
							lCatchClause.getException(), LOCAL_VAR_PREFIX);
					lookThroughStatement(node, lCatchClause.getBody());
				}
			} else if (statement instanceof AssertStatement) {
				AssertStatement lStatement = (AssertStatement) statement;
				lookThroughExpression(node, lStatement.getExpression());
				lookThroughExpression(node, lStatement.getMessage());
			} else {
				if (statement != null) {
					System.out.println("Statement of type " + statement.getClass().getName() + " not handled");
				}
			}
			// else if (statement instanceof TypeDeclarationStatement) {
			// TypeDeclarationStatement lStatement = (TypeDeclarationStatement)
			// statement;
			// lookThroughExpression(node, lStatement.getDeclaration().);
			// }

		}

		private void lookThroughSingleVariableDeclaration(
				MethodDeclaration node, SingleVariableDeclaration parameter, String pPrefix) {
			String name = parameter.getName().getIdentifier();

			if (hasToBeRenammed(name, pPrefix)) {
				AST ast = node.getAST();
				String lNewName = getRefactoredName(name, pPrefix);

				SingleVariableDeclaration fragment = ast
						.newSingleVariableDeclaration();

				// SingleVariableDeclaration fragment =
				// (SingleVariableDeclaration)
				// mASTRewrite.createCopyTarget(parameter);
				fragment.setName(ast.newSimpleName(lNewName));

				Type lType = (Type) mASTRewrite.createCopyTarget(parameter
						.getType());
				fragment.setType(lType);

				Expression lExpression = parameter.getInitializer();

				if (lExpression != null) {

					lookThroughExpression(node, lExpression);

					Expression lCopiedExpression = (Expression) mASTRewrite
							.createCopyTarget(lExpression);

					fragment.setInitializer(lCopiedExpression);
				}

				mASTRewrite.replace(parameter, fragment, null);
				mRefactoredVariables.put(name, lNewName);
			}
		}

		private void lookThroughVariableDeclarationFragment(
				MethodDeclaration node,
				VariableDeclarationFragment variableDeclarationFragment) {
			String name = variableDeclarationFragment.getName().getIdentifier();

			if (hasToBeRenammed(name, LOCAL_VAR_PREFIX)) {
				AST ast = node.getAST();
				String lNewName = getRefactoredName(name, LOCAL_VAR_PREFIX);

				// Field declaration
				VariableDeclarationFragment fragment = ast
						.newVariableDeclarationFragment();

				fragment.setName(ast.newSimpleName(lNewName));

				Expression lExpression = variableDeclarationFragment
						.getInitializer();

				if (lExpression != null) {

					Expression lRefactoredExpression = lookThroughExpression(node, lExpression);

					
					
					if (lRefactoredExpression != null) {
						fragment.setInitializer(lRefactoredExpression);
					} else {
						Expression lCopiedExpression = (Expression) mASTRewrite
								.createCopyTarget(lExpression);
						fragment.setInitializer(lCopiedExpression);
					}
					
				}

				mASTRewrite
						.replace(variableDeclarationFragment, fragment, null);
				mRefactoredVariables.put(name, lNewName);
			}
		}

		@Override
		public boolean visit(MethodDeclaration node) {
			List<Statement> lStatements = node.getBody().statements();
			
			// Refactor parameters
			List<SingleVariableDeclaration> parameters = node.parameters();
			for (SingleVariableDeclaration singleVariableDeclaration : parameters) {
				lookThroughSingleVariableDeclaration(node, singleVariableDeclaration, PARAMETER_PREFIX);
			}
			
			Javadoc lJavaDoc = node.getJavadoc();
			
			List<TagElement> tags = lJavaDoc.tags();
			for (TagElement tag : tags) {
				System.out.println(tag.getTagName());
				System.out.println(tag.getNodeType());
				if ("@param".equals(tag.getTagName())) {
					List<Expression> lFragments = tag.fragments();
					if (lFragments.size() > 0) {
						Expression lParameterName = lFragments.get(0);
						if (lParameterName instanceof SimpleName) {
							lookThroughExpression(node, lParameterName);
						}
					}
				}
				
			}
			
			// Refactor block statements
			for (Statement statement : lStatements) {
				try {
					lookThroughStatement(node, statement);
				} catch (Exception lE) {
					lE.printStackTrace();
					return false;
				}
			}
			methods.add(node);
			return super.visit(node);
		}

		public Expression lookThroughExpression(MethodDeclaration node,
				Expression pExpression) {
			Expression lResult = null;
			if (pExpression instanceof Assignment) {
				Assignment lAssignment = (Assignment) pExpression;
				lookThroughExpression(node, lAssignment.getLeftHandSide());
				lookThroughExpression(node, lAssignment.getRightHandSide());
			} else if (pExpression instanceof PostfixExpression) {
				PostfixExpression lPostfixExpression = (PostfixExpression) pExpression;
				lookThroughExpression(node, lPostfixExpression.getOperand());
			} else if (pExpression instanceof PrefixExpression) {
				PrefixExpression lExpression = (PrefixExpression) pExpression;
				lookThroughExpression(node, lExpression.getOperand());
			} else if (pExpression instanceof MethodInvocation) {
				MethodInvocation lMethodInvocation = (MethodInvocation) pExpression;
				List<Expression> lArguments = lMethodInvocation.arguments();
				for (Expression lExpression : lArguments) {
					lookThroughExpression(node, lExpression);
				}
				lookThroughExpression(node, lMethodInvocation.getExpression());
			} else if (pExpression instanceof SuperMethodInvocation) {
				SuperMethodInvocation lMethodInvocation = (SuperMethodInvocation) pExpression;
				List<Expression> lArguments = lMethodInvocation.arguments();
				for (Expression lExpression : lArguments) {
					lookThroughExpression(node, lExpression);
				}
			} else if (pExpression instanceof InfixExpression) {
				InfixExpression lInfixExpression = (InfixExpression) pExpression;
				lookThroughExpression(node, lInfixExpression.getLeftOperand());
				lookThroughExpression(node, lInfixExpression.getRightOperand());
				List<Expression> lExtendedOperands = lInfixExpression
						.extendedOperands();
				for (Expression expression : lExtendedOperands) {
					lookThroughExpression(node, expression);
				}
			} else if (pExpression instanceof ArrayAccess) {
				ArrayAccess lExpresion = (ArrayAccess) pExpression;
				lookThroughExpression(node, lExpresion.getArray());
				lookThroughExpression(node, lExpresion.getIndex());
			} else if (pExpression instanceof CastExpression) {
				CastExpression lExpresion = (CastExpression) pExpression;
				lookThroughExpression(node, lExpresion.getExpression());
			} else if (pExpression instanceof ConditionalExpression) {
				ConditionalExpression lExpresion = (ConditionalExpression) pExpression;
				lookThroughExpression(node, lExpresion.getExpression());
				lookThroughExpression(node, lExpresion.getThenExpression());
				lookThroughExpression(node, lExpresion.getElseExpression());
			} else if (pExpression instanceof FieldAccess) {
				FieldAccess lExpresion = (FieldAccess) pExpression;
				lookThroughExpression(node, lExpresion.getExpression());
				lookThroughExpression(node, lExpresion.getName());
			} else if (pExpression instanceof SuperFieldAccess) {
				SuperFieldAccess lExpresion = (SuperFieldAccess) pExpression;
				lookThroughExpression(node, lExpresion.getQualifier());
				lookThroughExpression(node, lExpresion.getName());
			} else if (pExpression instanceof InstanceofExpression) {
				InstanceofExpression lExpresion = (InstanceofExpression) pExpression;
				lookThroughExpression(node, lExpresion.getLeftOperand());
			} else if (pExpression instanceof VariableDeclarationExpression) {
				VariableDeclarationExpression lExpresion = (VariableDeclarationExpression) pExpression;
				List<VariableDeclarationFragment> lArguments = lExpresion
						.fragments();
				for (VariableDeclarationFragment lFragment : lArguments) {
					lookThroughVariableDeclarationFragment(node, lFragment);
				}
			} else if (pExpression instanceof ParenthesizedExpression) {
				ParenthesizedExpression lExpresion = (ParenthesizedExpression) pExpression;
				lookThroughExpression(node, lExpresion.getExpression());
			} else if (pExpression instanceof ArrayInitializer) {
				ArrayInitializer lExpresion = (ArrayInitializer) pExpression;
				List<Expression> lArguments = lExpresion.expressions();
				for (Expression lExpression : lArguments) {
					lookThroughExpression(node, lExpression);
				}
			} else if (pExpression instanceof ClassInstanceCreation) {
				ClassInstanceCreation lExpresion = (ClassInstanceCreation) pExpression;
				lookThroughExpression(node, lExpresion.getExpression());
				List<Expression> lArguments = lExpresion.arguments();
				for (Expression lExpression : lArguments) {
					lookThroughExpression(node, lExpression);
				}
			} else if (pExpression instanceof SimpleName) {
				SimpleName lSimpleName = (SimpleName) pExpression;
				String lIdentifier = lSimpleName.getIdentifier();
				if (mRefactoredVariables.containsKey(lIdentifier)) {
					SimpleName lNewSimpleName = mASTRewrite.getAST()
							.newSimpleName(
									mRefactoredVariables.get(lIdentifier));
					mASTRewrite.replace(lSimpleName, lNewSimpleName, null);
					lResult = lNewSimpleName;
				}
			} else if (pExpression instanceof QualifiedName) {
				QualifiedName lQualifiedName = (QualifiedName) pExpression;
				lookThroughExpression(node, lQualifiedName.getQualifier());
				// lookThroughExpression(node, lQualifiedName.getName());
			}
			return lResult;
		}

		public List<MethodDeclaration> getMethods() {
			return methods;
		}
	}

	private void fillListOfCompilationUnit(IPackageFragment pIPackageFragment,
			List<ICompilationUnit> pList) throws JavaModelException {
		for (ICompilationUnit lICompilationUnit : pIPackageFragment
				.getCompilationUnits()) {
			pList.add(lICompilationUnit);
		}
		IPackageFragmentRoot[] lIPackageFragmentRoots = mIPackageFragment
				.getJavaProject().getPackageFragmentRoots();
		List<IPackageFragment> lChildIPackageFragments = new ArrayList<IPackageFragment>();
		for (IPackageFragmentRoot lPackageFragmentRoot : lIPackageFragmentRoots) {
			IJavaElement[] lChildren = lPackageFragmentRoot.getChildren();
			for (IJavaElement lChild : lChildren) {
				if (lChild instanceof IPackageFragment) {
					String lPackageId = pIPackageFragment.getHandleIdentifier();
					if (!lChild.getHandleIdentifier().equals(lPackageId)
							&& lChild.getHandleIdentifier().startsWith(
									lPackageId)) {
						lChildIPackageFragments.add((IPackageFragment) lChild);
					}
				}
			}
		}
		for (IPackageFragment lPackageFragment : lChildIPackageFragments) {
			for (ICompilationUnit lICompilationUnit : lPackageFragment
					.getCompilationUnits()) {
				pList.add(lICompilationUnit);
			}
		}
	}

	public void setPackageFragment(IPackageFragment pIPackageFragment) {
		mIPackageFragment = pIPackageFragment;
		try {
			mICompilationUnits = new ArrayList<ICompilationUnit>();
			fillListOfCompilationUnit(pIPackageFragment, mICompilationUnits);
			System.out.println();
		} catch (JavaModelException e) {
			mICompilationUnits = new ArrayList<ICompilationUnit>();
		}

	}

	public IPackageFragment getPackageFragment() {
		return mIPackageFragment;
	}
}